﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Visitor
{
    class Program
    {
        static void Main(string[] args)
        {
            // Setup structure
            //ObjectStructure o = new ObjectStructure();
            //o.Attach(new Element1());
            //o.Attach(new Element2());
            //// Create visitor objects
            //ConcreteVisitor1 v1 = new ConcreteVisitor1();
            //ConcreteVisitor2 v2 = new ConcreteVisitor2();
            //// Structure accepting visitors
            //o.Accept(v1);
            //o.Accept(v2);

            Employees employees = new Employees();
            employees.Attach(new Clerk());
            employees.Attach(new Director());
            employees.Attach(new President());

            IncomeVisitor incomeVisitor = new IncomeVisitor();
            VacationVisitor vacationVisitor = new VacationVisitor();

            employees.Accept(incomeVisitor);
            employees.Accept(vacationVisitor);

            // Wait for user
            Console.ReadKey();
        }
    }

    /*abstract class Visitor
    {
        public abstract void VisitElement(Element element);
        //public abstract void VisitConcreteElemetA(Element element);
    }

    class ConcreteVisitor1 : Visitor
    {
        public override void VisitElement(Element element)
        {
            Console.WriteLine("{0} be visited by {1}",element.GetType().Name, this.GetType().Name);
        }
    }

    class ConcreteVisitor2 : Visitor
    {
        public override void VisitElement(Element element)
        {
            Console.WriteLine("{0} be visited by {1}", element.GetType().Name, this.GetType().Name);
        }
    }

    abstract class Element
    {
        public abstract void Accept(Visitor visitor);
    }

    class Element1 : Element
    {
        public override void Accept(Visitor visitor)
        {
            visitor.VisitElement(this);
        }
    }

    class Element2 : Element
    {
        public override void Accept(Visitor visitor)
        {
            visitor.VisitElement(this);
        }
    }

    class ObjectStructure
    {
        private List<Element> _list = new List<Element>();

        public void Attach(Element element)
        {
            _list.Add(element);
        }

        public void Detach(Element element)
        {
            _list.Remove(element);
        }

        public void Accept(Visitor visitor)
        {
            _list.ForEach((element) => { element.Accept(visitor); });            
        }
    }*/

    interface IVisitor
    {
        void Visit(IElement element);
    }

    class IncomeVisitor : IVisitor
    {
        public void Visit(IElement element)
        {
            Employee employee = element as Employee;

            // Provide 10% pay raise
            employee.Income *= 1.10;
            Console.WriteLine("{0} {1}'s new income: {2:C}",
              employee.GetType().Name, employee.Name,
              employee.Income);
        }
    }

    class VacationVisitor : IVisitor
    {
        public void Visit(IElement element)
        {
            Employee employee = element as Employee;

            // Provide 3 extra vacation days
            Console.WriteLine("{0} {1}'s new vacation days: {2}",
              employee.GetType().Name, employee.Name,
              employee.VacationDays);
        }
    }

    interface IElement
    {
        void Accept(IVisitor visitor);
    }

    class Employee : IElement
    {
        private string _name;
        private double _income;
        private int _vacationDays;

        // Constructor
        public Employee(string name, double income,
          int vacationDays)
        {
            this._name = name;
            this._income = income;
            this._vacationDays = vacationDays;
        }

        // Gets or sets the name
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        // Gets or sets income
        public double Income
        {
            get { return _income; }
            set { _income = value; }
        }

        // Gets or sets number of vacation days
        public int VacationDays
        {
            get { return _vacationDays; }
            set { _vacationDays = value; }
        }

        public void Accept(IVisitor visitor)
        {
            visitor.Visit(this);
        }

    }

    class Employees
    {
        private List<IElement> _list = new List<IElement>();

        public void Attach(IElement element)
        {
            _list.Add(element);
        }

        public void Detach(IElement element)
        {
            _list.Remove(element);
        }

        public void Accept(IVisitor visitor)
        {
            _list.ForEach((element) => { element.Accept(visitor); });
        }
    }

    // Three employee types
    class Clerk : Employee
    {
        // Constructor
        public Clerk()
            : base("Hank", 25000.0, 14)
        {
        }
    }

    class Director : Employee
    {
        // Constructor
        public Director()
            : base("Elly", 35000.0, 16)
        {
        }
    }

    class President : Employee
    {
        // Constructor
        public President()
            : base("Dick", 45000.0, 21)
        {
        }
    }
}
